﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using static VAsensorTest.FormMain;

namespace VAsensorTest
{
    public class VAsensor
    {
        public SerialPort serialPort;
        public string ComNameStr;
        public string BaudRateStr;
        public string ParityStr;
        public DateTime readStatusTime;
        private FormMain formMain;
        private System.Timers.Timer timer1;
        private ManualResetEvent _event = new ManualResetEvent(false);
        private ConcurrentQueue<byte[]> DataRecvQueue = new ConcurrentQueue<byte[]>();

        public VAsensor(SerialPort serialPort, string comName, string baudRate, string parity, FormMain formMain)
        {
            this.serialPort = serialPort;
            this.ComNameStr = comName;
            this.BaudRateStr = baudRate;
            this.ParityStr = parity;
            this.formMain = formMain;
            Thread thread = new Thread(threadRunning);
            thread.IsBackground = true;//设置线程为后台线程,那样进程里就不会有未关闭的程序了
            thread.Start();
            timer1 = new System.Timers.Timer();
            timer1.Interval = 500;
            //timer1.Start();
            timer1.Elapsed += new System.Timers.ElapsedEventHandler(WakeupThread);
            timer1.Enabled = false;
        }

        ~VAsensor()
        {
            if (timer1 != null)
            {
                try { timer1.Stop(); } catch { }
            }
        }

        /* 定时器定时执行，发送数据完成后，设定超时时间并开启定时器，同时休眠串口处理线程。应答接收完成后暂停定时器 */
        private void WakeupThread(object source, System.Timers.ElapsedEventArgs e)//定时器，用于定时唤醒threadRunning（串口通信的事务处理线程）
        {
            //Logger.WriteLog("Timer triggle set. Timer interval = " + timer1.Interval + "ms");//调试用的
            _event.Set(); //运行线程，_event.Set();用于代替thread.Resume();继续
        }

        /* 串口通信的事务处理线程。顺序执行和循环执行串口发送、等待接收、超时判断、接收解码。*/
        private void threadRunning()//串口通信的事务处理线程
        {
            while (true)
            {
                _event.WaitOne();//如果没有_event.Set();线程会一直阻塞在这里
                //Logger.WriteLog("Thread Running...>>>>>>>>>>>>>>>>>>");//调试用的
                //_event.WaitOne(int timespan);//线程会阻塞在这里timespan时间后，继续执行，单位为ms
                //_event.WaitOne(int timespan);//线程会阻塞在这里
                //_event.WaitOne(int timespan, bool setTrue);//线程会阻塞在这里timespan时间后，判断条件是否继续阻塞，=true表示等待_event.Set();函数触发继续，=false表示自动触发继续
                try
                {
                    //将接收到的数据取出
                    Pro_DataTakeOut_ThreadRunning();
                }
                catch (Exception err1)
                {
                    //出错，则写日志
                    Logger.WriteLog("[ComDataReceive][Error]-将接收到的数据取出时出错\r\n" + err1.ToString());
                }
                if (RecvDataLength > 22) //长度不足，则退出
                {
                    try
                    {
                        // 处理接收的数据，按照协议进行数据分析，提取正常的协议，抛弃无效数据，并且按照功能码处理
                        Pro_DataAnalyse_ThreadRunning();
                    }
                    catch (Exception err2)
                    {
                        Logger.WriteLog("[ComDataReceive][Error]-分析数据出错\r\n" + err2.ToString());//出错，则写日志
                    }
                }

                //Logger.WriteLog("Thread reset by itself. Because thread is complete.<<<<<<<<<<<<<<<<<<<");//调试用的
                _event.Reset();//让线程阻塞，用于代替Thread.Suspend()暂停
            }
        }

        /// <summary>
        /// 处理接收的数据，按照协议进行数据分析，提取正常的协议，抛弃无效数据，并且按照功能码处理
        /// </summary>
        private void Pro_DataAnalyse_ThreadRunning()
        {
            int index = 0;//处理指针
            byte add_crc = 0x00;//和校验
            byte xor_crc = 0x00;//异或
            int getKeyCount = 0;//找到关键词标志位，=0没有找到关键词，=1找到1#协议头，=2找到2#协议头，=3找到功能码，=4找到校验码，=5找到协议尾
            int i = 0;
            int fcIndex = 0;
            int endIndex = 0;
            int headIndex = 0;
            while (index < RecvDataLength)
            {
                if (getKeyCount == 0)//未找到头,则找头
                {
                    if (RecvBuf[index] == 0xAB)
                    {
                        getKeyCount = 1;
                        headIndex = index;
                    }
                }
                else if (getKeyCount == 1)//找到1#协议头，则找2#协议头
                {
                    if (RecvBuf[index] == 0xCD)
                    {
                        getKeyCount = 2;
                    }
                    else
                    {
                        add_crc = 0x00;
                        xor_crc = 0x00;
                        getKeyCount = 0;
                    }
                }
                else if (getKeyCount == 2)//找到2#协议头，则找功能码
                {
                    if ((byte)(RecvBuf[index] & 0xF0) == 0x30)//功能码为0x3?，则为正常返回值
                    {
                        fcIndex = index;
                        add_crc = (byte)(add_crc + RecvBuf[index]);
                        xor_crc = (byte)(xor_crc ^ RecvBuf[index]);
                        getKeyCount = 3;
                        i = 0;
                    }
                    else
                    {
                        add_crc = 0x00;
                        xor_crc = 0x00;
                        getKeyCount = 0;
                    }
                }
                else if (getKeyCount == 3)//找到功能码，则找校验码
                {
                    i++;
                    if (i >= 16)
                    {
                        if ((index + 2) >= RecvDataLength)
                            break;
                        add_crc = (byte)(add_crc + RecvBuf[index]);
                        xor_crc = (byte)(xor_crc ^ RecvBuf[index]);
                        if (add_crc == RecvBuf[index + 1] && xor_crc == RecvBuf[index + 2])
                        {
                            getKeyCount = 4;
                        }
                        else
                        {
                            add_crc = 0x00;
                            xor_crc = 0x00;
                            getKeyCount = 0;
                        }
                    }
                    else
                    {
                        add_crc = (byte)(add_crc + RecvBuf[index]);
                        xor_crc = (byte)(xor_crc ^ RecvBuf[index]);
                    }
                }
                else if (getKeyCount == 4)//找到检验码，则找协议尾
                {
                    i++;
                    if (i >= 19)
                    {
                        if (RecvBuf[index] == 0x3D)
                        {
                            getKeyCount = 5;
                        }
                        else
                        {
                            add_crc = 0x00;
                            xor_crc = 0x00;
                            getKeyCount = 0;
                        }
                    }
                }
                if (getKeyCount == 5)
                {
                    endIndex = index;
                    Pro_RecvCmd(fcIndex);
                    add_crc = 0x00;
                    xor_crc = 0x00;
                    getKeyCount = 0;
                }
                index++;
            }

            //缓冲区内的有效命令已经处理完成了
            //遍历这个缓冲区后，计算还剩下几个字节数据
            //移动整个缓冲区
            if (endIndex > 0)
            {
                int k;
                for (k = 0; endIndex < RecvDataLength; endIndex++)
                {
                    RecvBuf[k++] = RecvBuf[endIndex];
                }
                RecvDataLength = k;
            }
            else if (headIndex > 0)
            {
                int k;
                for (k = 0; headIndex < RecvDataLength; headIndex++)
                {
                    RecvBuf[k++] = RecvBuf[headIndex];
                }
                RecvDataLength = k;
            }
        }

        /// <summary>
        /// 将接收到的数据取出
        /// </summary>
        private void Pro_DataTakeOut_ThreadRunning()
        {
            while (!DataRecvQueue.IsEmpty)
            {
                byte[] tmpbyte;
                DataRecvQueue.TryDequeue(out tmpbyte);
                if ((tmpbyte.Length + RecvDataLength) >= RecvBuf.Length)
                {
                    Logger.WriteLog("[ComDataReceive][Error]-RecvBuf is full, Machine Have Some Error");//出错，则写日志
                    RecvDataLength = 0;
                    continue;
                }
                for (int j = 0; j < tmpbyte.Length; j++)
                {
                    RecvBuf[RecvDataLength++] = tmpbyte[j];//将临时缓冲区中的数据移至接收缓冲区
                }
            }
        }

        /// <summary>
        /// 打开串口
        /// </summary>
        public void OpenCom()
        {
            if (serialPort.IsOpen)//判断串口是否已经被打开了
            {
                Logger.WriteLog("[OpenCom][Error]-" + ComNameStr + " is opened");//出错，则写日志
                //ApsLocalSql.ErrorEventRecordList.Add(new ErrorEvent(BillPlace, positionName, BillPlace.ToString() + "-000", "CoinSend Com Port is Opened Error. Machine Amount = " + Amount));
                //Console.WriteLine(equipmentName + equipmentNum.ToString() + "-000" + "Com Port is Opened Error.");
            }
            else
            {
                if (ComNameStr.Length < 4)
                {
                    Logger.WriteLog("[OpenCom][Error]-串口名称不正确，应当为\"COMx\"，其中字符‘x’必须为数字");//出错，则写日志
                    return;
                }
                if (ComNameStr.IndexOf("COM") != 0)
                {
                    Logger.WriteLog("[OpenCom][Error]-串口名称不正确，应当为\"COMx\"，其中字符‘x’必须为数字");//出错，则写日志
                    return;
                }
                try//检查COM字符后面是否是正确的数字
                {
                    Convert.ToInt32(ComNameStr.Substring(3));
                }
                catch
                {
                    Logger.WriteLog("[OpenCom][Error]-串口名称不正确，应当为\"COMx\"，其中字符‘x’必须为数字");//出错，则写日志
                    return;
                }
                try
                {
                    serialPort.PortName = ComNameStr;
                }
                catch (Exception err1)
                {
                    Logger.WriteLog("[OpenCom][Error]-" + err1);//出错，则写日志
                    return;
                }
                serialPort.BaudRate = Convert.ToInt32(BaudRateStr);
                if (ParityStr.Equals("NONE"))
                    serialPort.Parity = Parity.None;
                else if (ParityStr.Equals("EVEN"))
                    serialPort.Parity = Parity.Even;
                else if (ParityStr.Equals("ODD"))
                    serialPort.Parity = Parity.Odd;
                else
                    serialPort.Parity = Parity.None;
                try
                {
                    serialPort.Open();//尝试打开串口
                    serialPort.DataReceived += new SerialDataReceivedEventHandler(Pro_DataReceived);//串口接收处理函数
                    timer1.Start();
                }
                catch (Exception err)
                {
                    Logger.WriteLog("[OpenCom][Error]-" + err);//出错，则写日志
                    //ApsLocalSql.ErrorEventRecordList.Add(new ErrorEvent(BillPlace, positionName, BillPlace.ToString() + "-004", "CoinSend Com Port Open Error. Machine Amount = " + Amount));
                    //Console.WriteLine(equipmentName + equipmentNum.ToString() + "-004" + "CoinSend Com Port Open Error. Machine Amount = " + Amount + ",error:" + err);
                }
            }
        }

        /// <summary>
        /// 关闭串口
        /// </summary>
        public void CloseCom()
        {
            if (serialPort.IsOpen)//判断串口是否已经被打开了
            {
                try
                {
                    serialPort.Close();
                }
                catch (Exception err)
                {
                    Logger.WriteLog("[CloseCom][Error]-" + err);//出错，则写日志
                    //Console.WriteLine(positionName + BillPlace.ToString() + "-004" + "CoinSend Com Port Close Error. Machine Amount = " + Amount + ",error:" + err);
                }
                try
                {
                    timer1.Stop();
                }
                catch (Exception err)
                {
                    Logger.WriteLog("[CloseCom][Error]-" + err);//出错，则写日志
                    //Console.WriteLine(positionName + BillPlace.ToString() + "-004" + "CoinSend Com Port Close Error. Machine Amount = " + Amount + ",error:" + err);
                }
            }
        }

        private byte[] RecvBuf = new byte[4000];//接收缓冲区
        private int RecvDataLength = 0;//接收缓冲区内有多少数据

        private void Pro_DataReceived(object sender, SerialDataReceivedEventArgs e)//串口接收处理函数
        {
            //string str = serialPort.ReadExisting();//字符串方式读
            if (serialPort.BytesToRead <= 0) return;
            byte[] tmpReceiveBuf = new byte[serialPort.BytesToRead];//接收到多少个字节的数据，则创建一个临时接收缓冲区
            serialPort.Read(tmpReceiveBuf, 0, tmpReceiveBuf.Length);//将收到的数据移动至临时接收缓冲区
            string s = "[收到数据] = ";
            StringBuilder ret = new StringBuilder();
            foreach (byte b in tmpReceiveBuf)
            {
                ret.AppendFormat("{0:X2} ", b);//{0:X2} 大写 //{0:x2} 小写
            }
            s += ret.ToString();
            Logger.WriteLog(s);//出错，则写日志
            DataRecvQueue.Enqueue(tmpReceiveBuf);//将本次接收到的数据移入队列，等待线程thread处理
            _event.Set(); //运行线程（线程会立即从阻塞态变为运行态），_event.Set();用于代替thread.Resume();继续
        }

        /* 字节数组转16进制字符串 */
        private string byteToHexStr(byte[] bytes)//字节数组转16进制字符串
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("X2") + ",";
                }
            }
            return returnStr;
        }

        #region 一堆根据功能码来处理返回的数据的函数

        /// <summary>
        /// 获取采样数据 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"></param>
        public void Pro_RecvCmd_30(int fcIndex)
        {
            string s1, s2, log, s3;
            s1 = "";
            for (int i = 0; i < 9; i++)
            {
                s1 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            
            s2 = "";
            for (int i = 9; i < 16; i++)
            {
                s2 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            s3 = "";
            decimal dat = 0.00M;
            try
            {
                s3 = s2.Substring(0, 5);
                if (s3.Substring(0,1).CompareTo("0") == 0 && s3.Substring(1, 1).CompareTo("0") == 0)
                {
                    s3 = s3.Substring(1);
                }
                dat = Convert.ToDecimal(s3);
                dat = Math.Round(1000 / dat, 2);//保留2位小数//dat = 1000 / dat;//
                s3 = dat.ToString("#0.00") + "Hz";
            }
            catch
            {
                Logger.WriteLog("【采样数据】接收到一帧电压或电流采样数据:" + s1 + s2 + "，但是解析数据时出错");
            }
            if (s1.Substring(s1.Length - 1, 1).CompareTo("V") == 0)
            {
                log = "【采样数据】接收到一帧电压采样数据：" + s1 + " ，周期：" + s2 + "，频率：" + s3;
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshLabel), new object[] { formMain.label_V_Data, s1 });
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshLabel), new object[] { formMain.label_V_Period, s2 }); 
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshLabel), new object[] { formMain.label_V_Frequency, s3 });
            }
            else
            {
                log = "【采样数据】接收到一帧电流采样数据：" + s1 + " ，周期：" + s2 + "，频率：" + s3;
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshLabel), new object[] { formMain.label_A_Data, s1 });
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshLabel), new object[] { formMain.label_A_Period, s2 });
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshLabel), new object[] { formMain.label_A_Frequency, s3 });
            }
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 设置电压/电流传感器的量程和输出阈值参数 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"> 功能码在接收数据缓冲区的指针 </param>
        /// <param name="Vtrue_Afalse_flag"> =true处理电压传感器；=false处理电流传感器 </param>
        public void Pro_RecvCmd_31_33(int fcIndex, bool Vtrue_Afalse_flag)
        {
            string s1, s2, log;
            s1 = "";
            for (int i = 0; i < 9; i++)
            {
                s1 += (char)(RecvBuf[fcIndex + 1 + i]);
            }

            s2 = "";
            for (int i = 9; i < 16; i++)
            {
                s2 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            if (Vtrue_Afalse_flag)
                log = "【下载电压传感器参数】接收到应答：电压传感器量程=" + s1 + " ，电压传感器输出阈值=" + s2;
            else
                log = "【下载电流传感器参数】接收到应答：电流传感器量程=" + s1 + " ，电流传感器输出阈值=" + s2;
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 设置电压/电流传感器的量程和输出阈值参数 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"> 功能码在接收数据缓冲区的指针 </param>
        /// <param name="Vtrue_Afalse_flag"> =true处理电压传感器；=false处理电流传感器 </param>
        public void Pro_RecvCmd_32_34(int fcIndex, bool Vtrue_Afalse_flag)
        {
            string s1, s2, log;
            s1 = "";
            for (int i = 0; i < 9; i++)
            {
                s1 += (char)(RecvBuf[fcIndex + 1 + i]);
            }

            s2 = "";
            for (int i = 9; i < 16; i++)
            {
                s2 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            if (Vtrue_Afalse_flag)
            {
                log = "【回读电压传感器参数】接收到应答：电压传感器量程=" + s1 + " ，电压传感器输出阈值=" + s2;
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_V_Param1, s1 });
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_V_Param2, s2 });
            }
            else
            {
                log = "【回读电流传感器参数】接收到应答：电流传感器量程=" + s1 + " ，电流传感器输出阈值=" + s2;
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_A_Param1, s1 });
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_A_Param2, s2 });
            }
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 自动出厂零点校正 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"> 功能码在接收数据缓冲区的指针 </param>
        public void Pro_RecvCmd_35(int fcIndex)
        {
            string log;
            string n1 = "";
            string n2 = "";
            int num1 = 0, num2 = 0;
            for (int i = 0; i < 8; i++)
            {
                n1 += (char)(RecvBuf[fcIndex + 1 + i]);
            }

            for (int i = 8; i < 16; i++)
            {
                n2 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            try
            {
                num1 = Convert.ToInt32(n1);
                num2 = Convert.ToInt32(n2);
            }
            catch
            {
                Logger.WriteLog("【自动出厂零点校正】接收到一帧零点校正数据:" + n1 + ", " +  n2 + "，但是解析数据时出错");
            }
            log = "【自动出厂零点校正】接收到应答：电压零点校正参数=" + num1 + " ，电流零点校正参数=" + num2;
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 回读/修改零点校正参数 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"> 功能码在接收数据缓冲区的指针 </param>
        /// <param name="GetTrue_SetFalse_flag"> =true处理回读；=false处理下载指令 </param>
        public void Pro_RecvCmd_36_37(int fcIndex, bool GetTrue_SetFalse_flag)
        {
            string log;
            string n1 = "";
            string n2 = "";
            int num1 = 0, num2 = 0;
            for (int i = 0; i < 8; i++)
            {
                n1 += (char)(RecvBuf[fcIndex + 1 + i]);
            }

            for (int i = 8; i < 16; i++)
            {
                n2 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            try
            {
                num1 = Convert.ToInt32(n1);
                num2 = Convert.ToInt32(n2);
            }
            catch
            {
                Logger.WriteLog("【自动出厂零点校正】接收到一帧零点校正数据:" + n1 + ", " + n2 + "，但是解析数据时出错");
            }
            if (GetTrue_SetFalse_flag)
            {
                log = "【回读零点校正参数】接收到应答：电压零点校正参数=" + num1 + " ，电流零点校正参数=" + num2;
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_V_ZeroData, num1 });
                formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_A_ZeroData, num2 });
            }
            else
            {
                log = "【修改零点校正参数】接收到应答：电压零点校正参数=" + num1 + " ，电流零点校正参数=" + num2;
            }
            
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 下载/回读主动上传间隔 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"> 功能码在接收数据缓冲区的指针 </param>
        /// <param name="GetTrue_SetFalse_flag"> =true处理回读；=false处理下载指令 </param>
        public void Pro_RecvCmd_38_39(int fcIndex, bool GetTrue_SetFalse_flag)
        {
            string log, s1 = "";
            for (int i = 0; i < 8; i++)
            {
                s1 += (char)(RecvBuf[fcIndex + 1 + i]);
            }
            if (GetTrue_SetFalse_flag)
            {
                try
                {
                    log = "【回读主动上传间隔】接收到应答：主动上传间隔=" + s1;
                    int number = Convert.ToInt32(s1.Substring(0, s1.Length - 2)); // 最后面两个字符为ms，因此删除这两个字符
                    formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_UploadTime, number.ToString() });
                }
                catch
                {
                    log = "【回读主动上传间隔】接收到应答：主动上传间隔=" + s1;
                    formMain.Invoke(new delegate_RefreshUI(formMain.refreshTextBox), new object[] { formMain.textBox_UploadTime, s1 });
                }
            }
            else
            {
                log = "【回读主动上传间隔】接收到应答：主动上传间隔=" + s1;
            }
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 读取固件版本号 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"> 功能码在接收数据缓冲区的指针 </param>
        public void Pro_RecvCmd_3A(int fcIndex)
        {
            string log;
            string s = "";
            for (int i = 0; i < 16; i++)
            {
                if (RecvBuf[fcIndex + 1 + i] != 0x20)
                {
                    s += (char)(RecvBuf[fcIndex + 1 + i]);
                }
            }
            log = "【读取固件版本号】接收到应答：版本号=" + s;
            Logger.WriteLog(log);
        }

        /// <summary>
        /// 复位采集器 处理返回的数据
        /// </summary>
        public void Pro_RecvCmd_3B()
        {
            Logger.WriteLog("【复位采集器】接收到应答：复位完成");
        }

        /// <summary>
        /// 处理返回的数据
        /// </summary>
        /// <param name="fcIndex"></param>
        public void Pro_RecvCmd(int fcIndex)
        {
            switch (RecvBuf[fcIndex])
            {
                case 0x30://获取采样数据
                    Pro_RecvCmd_30(fcIndex);
                    break;
                case 0x31://设置电压传感器的量程和输出阈值参数
                    Pro_RecvCmd_31_33(fcIndex, true);
                    break;
                case 0x32://读取电压传感器的量程和输出阈值参数
                    Pro_RecvCmd_32_34(fcIndex, true);
                    break;
                case 0x33://设置电流传感器的量程和输出阈值参数
                    Pro_RecvCmd_31_33(fcIndex, false);
                    break;
                case 0x34://读取电流传感器的量程和输出阈值参数
                    Pro_RecvCmd_32_34(fcIndex, false);
                    break;
                case 0x35://自动出厂零点校正
                    Pro_RecvCmd_35(fcIndex);
                    break;
                case 0x36://读取出厂零点校正参数
                    Pro_RecvCmd_36_37(fcIndex, true);
                    break;
                case 0x37://修改出厂零点校正参数
                    Pro_RecvCmd_36_37(fcIndex, false);
                    break;
                case 0x38://下载主动上传间隔
                    Pro_RecvCmd_38_39(fcIndex, false);
                    break;
                case 0x39://读取主动上传间隔
                    Pro_RecvCmd_38_39(fcIndex, true);
                    break;
                case 0x3A://读取固件版本号
                    Pro_RecvCmd_3A(fcIndex);
                    break;
                case 0x3B://复位采集器
                    Pro_RecvCmd_3B();
                    break;
                default:
                    Logger.WriteLog("[ComDataReceive][Error]-Function code is error.");
                    break;
            }
        }

        #endregion

        #region 一堆通过串口向采集器发送数据的函数
        // 计算发送指令中的累加校验码
        byte countSendAddCrc(byte[] buf)
        {
            byte crcdat = 0x00;
            if (buf.Length < 6) return crcdat;
            for (int i = 2; i < buf.Length - 3; i++)
            {
                crcdat = (byte)(crcdat + buf[i]);
            }
            return crcdat;
        }

        // 计算发送指令中的异或校验码
        byte countSendXorCrc(byte[] buf)
        {
            byte crcdat = 0x00;
            if (buf.Length < 6) return crcdat;
            for (int i = 2; i < buf.Length - 3; i++)
            {
                crcdat = (byte)(crcdat ^ buf[i]);
            }
            return crcdat;
        }

        /// <summary>
        /// 通用，无数据变化的指令发送
        /// </summary>
        /// <param name="fcDat"> 指令数据中的功能码，取值范围0x40-0x4B </param>
        private void Common_Data_Send(byte fcDat)
        {
            byte[] sendDataBuf = new byte[] { 0xAB, 0xCD, 0x4A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x3D };
            sendDataBuf[2] = fcDat;
            sendDataBuf[sendDataBuf.Length - 3] = countSendAddCrc(sendDataBuf);
            sendDataBuf[sendDataBuf.Length - 2] = countSendXorCrc(sendDataBuf);
            Pro_DataSend(sendDataBuf);
        }

        
        /// <summary>
        /// 获取采样数据
        /// </summary>
        public void GetAdcData()
        {
            Common_Data_Send(0x40);
        }

        /// <summary>
        /// 下载电压传感器参数
        /// </summary>
        public void SetVsensorParam(string s)
        {
            byte[] sendDataBuf = new byte[] { 0xAB, 0xCD, 0x41, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x3D };
            if (s.Length != 16)
            {
                Logger.WriteLog("参数：" + s + ", 内容不符合规范\r\n");
                return;
            }
            for (int i = 0; i < s.Length; i++)
            {
                sendDataBuf[i + 3] = (byte)(s[i]);
            }
            sendDataBuf[sendDataBuf.Length - 3] = countSendAddCrc(sendDataBuf);
            sendDataBuf[sendDataBuf.Length - 2] = countSendXorCrc(sendDataBuf);
            Pro_DataSend(sendDataBuf);
        }

        /// <summary>
        /// 回读电压传感器参数
        /// </summary>
        public void GetVsensorParam()
        {
            Common_Data_Send(0x42);
        }

        /// <summary>
        /// 下载电流传感器参数
        /// </summary>
        /// <param name="s"></param>
        public void SetAsensorParam(string s)
        {
            byte[] sendDataBuf = new byte[] { 0xAB, 0xCD, 0x43, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x3D };
            if (s.Length != 16)
            {
                Logger.WriteLog("参数：" + s + ", 内容不符合规范\r\n");
                return;
            }
            for (int i = 0; i < s.Length; i++)
            {
                sendDataBuf[i + 3] = (byte)(s[i]);
            }
            sendDataBuf[sendDataBuf.Length - 3] = countSendAddCrc(sendDataBuf);
            sendDataBuf[sendDataBuf.Length - 2] = countSendXorCrc(sendDataBuf);
            Pro_DataSend(sendDataBuf);
        }

        /// <summary>
        /// 回读电流传感器参数
        /// </summary>
        public void GetAsensorParam()
        {
            Common_Data_Send(0x44);
        }

        /// <summary>
        /// 自动出厂零点校正
        /// </summary>
        public void AutoCalibrate()
        {
            Common_Data_Send(0x45);
        }

        /// <summary>
        /// 下载零点校正参数
        /// </summary>
        public void SetCalibrate(int v_dat, int a_dat)
        {
            int i = 3;
            byte[] sendDataBuf = new byte[] { 0xAB, 0xCD, 0x47, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x3D };
            v_dat %= 100000000;
            sendDataBuf[i++] = (byte)((v_dat / 10000000) + 0x30);
            v_dat %= 10000000;
            sendDataBuf[i++] = (byte)((v_dat / 1000000) + 0x30);
            v_dat %= 1000000;
            sendDataBuf[i++] = (byte)((v_dat / 100000) + 0x30);
            v_dat %= 100000;
            sendDataBuf[i++] = (byte)((v_dat / 10000) + 0x30);
            v_dat %= 10000;
            sendDataBuf[i++] = (byte)((v_dat / 1000) + 0x30);
            v_dat %= 1000;
            sendDataBuf[i++] = (byte)((v_dat / 100) + 0x30);
            v_dat %= 100;
            sendDataBuf[i++] = (byte)((v_dat / 10) + 0x30);
            v_dat %= 10;
            sendDataBuf[i++] = (byte)(v_dat + 0x30);
            a_dat %= 100000000;
            sendDataBuf[i++] = (byte)((a_dat / 10000000) + 0x30);
            a_dat %= 10000000;
            sendDataBuf[i++] = (byte)((a_dat / 1000000) + 0x30);
            a_dat %= 1000000;
            sendDataBuf[i++] = (byte)((a_dat / 100000) + 0x30);
            a_dat %= 100000;
            sendDataBuf[i++] = (byte)((a_dat / 10000) + 0x30);
            a_dat %= 10000;
            sendDataBuf[i++] = (byte)((a_dat / 1000) + 0x30);
            a_dat %= 1000;
            sendDataBuf[i++] = (byte)((a_dat / 100) + 0x30);
            a_dat %= 100;
            sendDataBuf[i++] = (byte)((a_dat / 10) + 0x30);
            a_dat %= 10;
            sendDataBuf[i++] = (byte)(a_dat + 0x30);
            sendDataBuf[sendDataBuf.Length - 3] = countSendAddCrc(sendDataBuf);
            sendDataBuf[sendDataBuf.Length - 2] = countSendXorCrc(sendDataBuf);
            Pro_DataSend(sendDataBuf);
        }

        /// <summary>
        /// 回读零点校正参数
        /// </summary>
        public void GetCalibrate()
        {
            Common_Data_Send(0x46);
        }

        /// <summary>
        /// 下载主动上传间隔
        /// </summary>
        /// <param name="time_ms"></param>
        public void SetUploadTime(int time_ms)
        {
            int i = 3;
            byte[] sendDataBuf = new byte[] { 0xAB, 0xCD, 0x48, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x3D };
            time_ms %= 1000000;
            sendDataBuf[i++] = (byte)((time_ms / 100000) + 0x30);
            time_ms %= 100000;
            sendDataBuf[i++] = (byte)((time_ms / 10000) + 0x30);
            time_ms %= 10000;
            sendDataBuf[i++] = (byte)((time_ms / 1000) + 0x30);
            time_ms %= 1000;
            sendDataBuf[i++] = (byte)((time_ms / 100) + 0x30);
            time_ms %= 100;
            sendDataBuf[i++] = (byte)((time_ms / 10) + 0x30);
            time_ms %= 10;
            sendDataBuf[i++] = (byte)(time_ms + 0x30);
            sendDataBuf[i++] = (byte)('m');
            sendDataBuf[i++] = (byte)('s');
            sendDataBuf[sendDataBuf.Length - 3] = countSendAddCrc(sendDataBuf);
            sendDataBuf[sendDataBuf.Length - 2] = countSendXorCrc(sendDataBuf);
            Pro_DataSend(sendDataBuf);
        }

        /// <summary>
        /// 回读主动上传间隔
        /// </summary>
        public void GetUploadTime()
        {
            byte[] sendDataBuf = new byte[] { 0xAB, 0xCD, 0x49, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x3D };
            sendDataBuf[sendDataBuf.Length - 3] = countSendAddCrc(sendDataBuf);
            sendDataBuf[sendDataBuf.Length - 2] = countSendXorCrc(sendDataBuf);
            Pro_DataSend(sendDataBuf);
        }

        /// <summary>
        /// 回读固件版本号
        /// </summary>
        public void GetVersion()
        {
            Common_Data_Send(0x4A);
        }

        /// <summary>
        /// 复位采集器
        /// </summary>
        public void Reset()
        {
            Common_Data_Send(0x4B);
        }

        /// <summary>
        /// 串口发送数据
        /// </summary>
        /// <param name="buf"></param>
        private void Pro_DataSend(byte[] buf)
        {
            if (!serialPort.IsOpen)
            {
                OpenCom();
            }
            try
            {
                serialPort.Write(buf, 0, buf.Length);
                string s = "(发送数据) = ";
                StringBuilder ret = new StringBuilder();
                foreach (byte b in buf)
                {
                    ret.AppendFormat("{0:X2} ", b);//{0:X2} 大写 //{0:x2} 小写
                }
                s += ret.ToString();
                Logger.WriteLog(s);//出错，则写日志
            }
            catch (Exception e)
            {
                Logger.WriteLog("[ComDataSend][Error]-" + e);//出错，则写日志
                //ApsLocalSql.ErrorEventRecordList.Add(new ErrorEvent(BillPlace, positionName, BillPlace.ToString() + "-003", "CoinSend Com SendData Error. Machine Amount = " + Amount));
                //Console.WriteLine(positionName + BillPlace.ToString() + "-003" + "CoinSend Com SendData Error. Machine Amount = " + Amount + ",error:" + e);
            }
        }

        #endregion
    }
}
